/*
Autor: Alexander Savochkin, 2010
New BSD License
*/

#include "Config.h"
#include "Exception.h"

#include <set>
#include <map>
#include <iterator>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/foreach.hpp>
#include <boost/array.hpp>

#include <iostream>

using boost::property_tree::ptree;
using std::set;
using std::map;
using std::back_inserter;

namespace SRCLF {
namespace Config {

	void Section::type2Name(Type type, string& name)
	{
		if(type == channel)
			name = "channel";
		else if(type == input)
			name = "input";
		else if(type == output)
			name = "output";
		else if(type == regulator)
			name = "regulator";
		else if(type == resource)
			name = "resource";
		else if(type == server)
			name = "server";
	}

	Section::Type Section::name2Type(const string& name)
	{
		if(name == "channel")
			return Section::channel;
		else if(name == "input")
			return Section::input;
		else if(name == "output")
			return Section::output;
		else if(name == "regulator")
			return Section::regulator;
		else if(name == "resource")
			return Section::resource;
		else if(name == "server")
			return Section::server;
		else
			throw ConfigException("Unknown section type: " + name);
	}

	size_t Section::keyCount(const string& key) const
	{
		map<string, ConfigValue>::const_iterator it = attributes.find(key);
		if(it == attributes.end())
			return 0;
		return it->second.size();
	}

	const string& Section::getScalarByKey(const string& key) const  //throws exception ... if key odes not exist or there are multiple values 
	{
		map<string, ConfigValue>::const_iterator it = attributes.find(key);
		if (it == attributes.end() || it->second.size() != 1)
			throw ConfigException("Exactly one value for key '" + key + "' expected");
		return it->second[0];
	}

	const vector<string>& Section::getAllValuesByKey(const string& key) const
	{
		map<string, ConfigValue>::const_iterator it = attributes.find(key);
		if (it == attributes.end())
			throw ConfigException("One or multiple values for key '" + key + "' in section " + getName() + "expected");
		return it->second;
	}


	void Section::applyInheritanceFromSection(Section& parent_section)
	{
		set<string> current_keys;
		BOOST_FOREACH(KeyValue kv, attributes)
			current_keys.insert(kv.first);
		BOOST_FOREACH(const KeyValue kv, parent_section.attributes)
		{
			//If key is not present in current section, copy it from parent section
			if(current_keys.count(kv.first) == 0)
				attributes.insert(kv);
		}
	}


	void Section::fillConfigSection(const string& stype, const ptree &section_subtree) 
	{
		type = name2Type(stype);

		parent = section_subtree.get_optional<string>("parent");
		class_name = section_subtree.get_optional<string>("class");
		name = section_subtree.get<string>("name");	//Mandatory: throw excpetion if not found

		ptree::const_iterator iter = section_subtree.begin();
		const ptree& properties = section_subtree.get_child("attributes");
		BOOST_FOREACH(const ptree::value_type &v, properties)
		{
			string value = v.second.data();
			attributes[v.first].push_back(value);
		}
	}

	string Section::getClassName() const 
	{ 
		if (!class_name)
			throw ConfigException("Registered class name expected in section " + getName());
		return class_name.get();
	};

	void Configuration::readConfigFile( const string& filename )
	{
		std::ifstream config_file_stream;
		config_file_stream.open( filename.c_str() );
		if ( config_file_stream.fail() )
			throw ConfigException("Config file: " + filename + " does not exists" );
		readConfig( config_file_stream );
		config_file_stream.close();	
	};

	void Configuration::readConfig( std::istream& config_stream )
	{
		ptree property_tree;
		boost::property_tree::info_parser::read_info(config_stream, property_tree);
		initConfig(property_tree);
	};

	void Configuration::initConfig( const ptree& property_tree )
	{
		BOOST_FOREACH(const ptree::value_type &v,
				property_tree)
		{
			Section section;
			section.fillConfigSection(v.first, v.second);

			SectionsKey sec_key = make_pair( section.getType(), section.getName() );
			if(sections.count( sec_key ) != 0) //Check if section with same name and type already exists
			{
				string stype;
				Section::type2Name(sec_key.first, stype);
				throw ConfigException( "Config section with type " + stype + " and name " + sec_key.second + " already exists" );
			}
			sections[sec_key] = section;
		}
	}

	void Configuration::processInheritace()
	{
		for( Sections::iterator it = sections.begin(); it != sections.end(); ++it)
			processInheritaceForSection(it->second);
	}

	void Configuration::processInheritaceForSection(Section& section)
	{
		if (!section.getParent())
			return;

		Section& parent_section = _getSection( section.getType(), get(section.parent) );

		if ( section.getType() != parent_section.getType() )
			throw ConfigException("Error in config sections inheritance. Types not match");	//TODO fill error message

		if (parent_section.parent)
		{
			Section& parent_parent_section = _getSection( section.getType(), get(parent_section.parent) );
			processInheritaceForSection(parent_parent_section);
		}

		section.parent = boost::none;
		section.applyInheritanceFromSection( parent_section );
	}

	const Section& Configuration::getSection(Section::Type type, const string& name) const
	{
		SectionsKey sk = make_pair(type, name);
		map< SectionsKey, Section>::const_iterator skit;
		if( (skit = sections.find(sk)) != sections.end() )
			return skit->second;
		else
		{
			string stype;
			Section::type2Name(type, stype);
			throw ConfigException("Incorrect name and type pair in Configuration::getSection: Can't find section " + name + " of type " + stype);
		}
	}

	Section& Configuration::_getSection(Section::Type type, const string& name)
	{
		SectionsKey sk = make_pair(type, name);
		map< SectionsKey, Section>::iterator skit;
		if( (skit = sections.find(sk)) != sections.end() )
			return skit->second;
		else
		{
			string stype;
			Section::type2Name(type, stype);
			throw ConfigException("Incorrect name and type pair in Configuration::getSection: Can't find section " + name + " of type " + stype);
		}
	}
} //namespace Config
} //namespace SRCLF

